package com.gitee.wsl.transform.validator.sample

import com.gitee.wsl.base.Objects
import com.gitee.wsl.ext.base.SPACE
import com.gitee.wsl.ext.base.today
import com.gitee.wsl.ext.string.charAt
import com.gitee.wsl.text.regex.sample.OnlyNumbersRule
import com.gitee.wsl.time.T
import com.gitee.wsl.time.YEAR_MONTH
import com.gitee.wsl.time.yyMMdd
import kotlinx.datetime.LocalDate
import kotlinx.datetime.minus


/**
 * 身份证相关工具类<br></br>
 * see [https://www.oschina.net/code/snippet_1611_2881](https://www.oschina.net/code/snippet_1611_2881)
 *
 *
 *
 * 本工具并没有对行政区划代码做校验，如有需求，请参阅（2018年10月）：
 * [http://www.mca.gov.cn/article/sj/xzqh/2018/201804-12/20181011221630.html](http://www.mca.gov.cn/article/sj/xzqh/2018/201804-12/20181011221630.html)
 *
 *
 * @author Looly
 * @since 3.0.4
 */
object IdcardUtil {
    /**
     * 中国公民身份证号码最小长度。
     */
    private const val CHINA_ID_MIN_LENGTH = 15

    /**
     * 中国公民身份证号码最大长度。
     */
    private const val CHINA_ID_MAX_LENGTH = 18

    /**
     * 每位加权因子
     */
    private val POWER = intArrayOf(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)

    /**
     * 省市代码表
     */
    private val CITY_CODES = HashMap<String, String>()

    /**
     * 台湾身份首字母对应数字
     */
    private val TW_FIRST_CODE = HashMap<Char, Int>()

    init {
        CITY_CODES.put("11", "北京")
        CITY_CODES.put("12", "天津")
        CITY_CODES.put("13", "河北")
        CITY_CODES.put("14", "山西")
        CITY_CODES.put("15", "内蒙古")
        CITY_CODES.put("21", "辽宁")
        CITY_CODES.put("22", "吉林")
        CITY_CODES.put("23", "黑龙江")
        CITY_CODES.put("31", "上海")
        CITY_CODES.put("32", "江苏")
        CITY_CODES.put("33", "浙江")
        CITY_CODES.put("34", "安徽")
        CITY_CODES.put("35", "福建")
        CITY_CODES.put("36", "江西")
        CITY_CODES.put("37", "山东")
        CITY_CODES.put("41", "河南")
        CITY_CODES.put("42", "湖北")
        CITY_CODES.put("43", "湖南")
        CITY_CODES.put("44", "广东")
        CITY_CODES.put("45", "广西")
        CITY_CODES.put("46", "海南")
        CITY_CODES.put("50", "重庆")
        CITY_CODES.put("51", "四川")
        CITY_CODES.put("52", "贵州")
        CITY_CODES.put("53", "云南")
        CITY_CODES.put("54", "西藏")
        CITY_CODES.put("61", "陕西")
        CITY_CODES.put("62", "甘肃")
        CITY_CODES.put("63", "青海")
        CITY_CODES.put("64", "宁夏")
        CITY_CODES.put("65", "新疆")
        CITY_CODES.put("71", "台湾")
        CITY_CODES.put("81", "香港")
        CITY_CODES.put("82", "澳门")
        //issue#1277，台湾身份证号码以83开头，但是行政区划为71
        CITY_CODES.put("83", "台湾")
        CITY_CODES.put("91", "国外")

        TW_FIRST_CODE.put('A', 10)
        TW_FIRST_CODE.put('B', 11)
        TW_FIRST_CODE.put('C', 12)
        TW_FIRST_CODE.put('D', 13)
        TW_FIRST_CODE.put('E', 14)
        TW_FIRST_CODE.put('F', 15)
        TW_FIRST_CODE.put('G', 16)
        TW_FIRST_CODE.put('H', 17)
        TW_FIRST_CODE.put('J', 18)
        TW_FIRST_CODE.put('K', 19)
        TW_FIRST_CODE.put('L', 20)
        TW_FIRST_CODE.put('M', 21)
        TW_FIRST_CODE.put('N', 22)
        TW_FIRST_CODE.put('P', 23)
        TW_FIRST_CODE.put('Q', 24)
        TW_FIRST_CODE.put('R', 25)
        TW_FIRST_CODE.put('S', 26)
        TW_FIRST_CODE.put('T', 27)
        TW_FIRST_CODE.put('U', 28)
        TW_FIRST_CODE.put('V', 29)
        TW_FIRST_CODE.put('X', 30)
        TW_FIRST_CODE.put('Y', 31)
        TW_FIRST_CODE.put('W', 32)
        TW_FIRST_CODE.put('Z', 33)
        TW_FIRST_CODE.put('I', 34)
        TW_FIRST_CODE.put('O', 35)
    }

    /**
     * 将15位身份证号码转换为18位
     *
     * @param idCard 15位身份编码
     * @return 18位身份编码
     */
    fun convert15To18(idCard: String): String? {
        val idCard18 =  StringBuilder()
        if (idCard.length != CHINA_ID_MIN_LENGTH) {
            return null
        }
        if (Regex.OnlyNumbersRule.matches(idCard)) {
            // 获取出生年月日
            val birthday = idCard.substring(6, 12)
            T.Formats.YEAR_MONTH
            val birthDate = LocalDate.Formats.yyMMdd.parse(birthday)
            // 获取出生年(完全表现形式,如：2010)
            var sYear: Int = birthDate.year
            if (sYear > 2000) {
                // 2000年之后不存在15位身份证号，此处用于修复此问题的判断
                sYear -= 100
            }
            idCard18.append(idCard, 0, 6).append(sYear).append(idCard.substring(8))
            // 获取校验位
            val sVal = getCheckCode18(idCard18.toString())
            idCard18.append(sVal)
        } else {
            return null
        }
        return idCard18.toString()
    }

    /**
     * 将18位身份证号码转换为15位
     *
     * @param  idCard 18位身份编码
     * @return 15位身份编码
     */
    fun convert18To15(idCard: String?): String? {
        if (idCard.isNullOrBlank() && isValidCard18(idCard)) {
            return idCard!!.substring(0, 6) + idCard.substring(8, idCard.length - 1)
        }
        return idCard
    }

    /**
     * 是否有效身份证号，忽略X的大小写<br></br>
     * 如果身份证号码中含有空格始终返回`false`
     *
     * @param idCard 身份证号，支持18位、15位和港澳台的10位
     * @return 是否有效
     */
    fun isValidCard(idCard: String): Boolean {
        if (idCard.isBlank()) {
            return false
        }

        //idCard = idCard.trim();
        val length = idCard.length
        when (length) {
            18 -> return isValidCard18(idCard)
            15 -> return isValidCard15(idCard)
            10 -> {
                // 10位身份证，港澳台地区
                val cardVal = isValidCard10(idCard)
                return null != cardVal && "true" == cardVal[2]
            }

            else -> return false
        }
    }

    /**
     *
     *
     * 判断18位身份证的合法性
     *
     * 根据〖中华人民共和国国家标准GB11643-1999〗中有关公民身份号码的规定，公民身份号码是特征组合码，由十七位数字本体码和一位数字校验码组成。<br></br>
     * 排列顺序从左至右依次为：六位数字地址码，八位数字出生日期码，三位数字顺序码和一位数字校验码。
     *
     *
     * 顺序码: 表示在同一地址码所标识的区域范围内，对同年、同月、同 日出生的人编定的顺序号，顺序码的奇数分配给男性，偶数分配 给女性。
     *
     *
     *  1. 第1、2位数字表示：所在省份的代码
     *  1. 第3、4位数字表示：所在城市的代码
     *  1. 第5、6位数字表示：所在区县的代码
     *  1. 第7~14位数字表示：出生年、月、日
     *  1. 第15、16位数字表示：所在地的派出所的代码
     *  1. 第17位数字表示性别：奇数表示男性，偶数表示女性
     *  1. 第18位数字是校检码，用来检验身份证的正确性。校检码可以是0~9的数字，有时也用x表示
     *
     *
     *
     * 第十八位数字(校验码)的计算方法为：
     *
     *  1. 将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为：7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
     *  1. 将这17位数字和系数相乘的结果相加
     *  1. 用加出来和除以11，看余数是多少
     *  1. 余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2
     *  1. 通过上面得知如果余数是2，就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10，身份证的最后一位号码就是2
     *
     *
     *  1. 香港人在大陆的身份证，【810000】开头；同样可以直接获取到 性别、出生日期
     *  1. 81000019980902013X: 文绎循 男 1998-09-02
     *  1. 810000201011210153: 辛烨 男 2010-11-21
     *
     *
     *  1. 澳门人在大陆的身份证，【820000】开头；同样可以直接获取到 性别、出生日期
     *  1. 820000200009100032: 黄敬杰 男 2000-09-10
     *
     *
     *  1. 台湾人在大陆的身份证，【830000】开头；同样可以直接获取到 性别、出生日期
     *  1. 830000200209060065: 王宜妃 女 2002-09-06
     *  1. 830000194609150010: 苏建文 男 1946-09-14
     *  1. 83000019810715006X: 刁婉琇 女 1981-07-15
     *
     *
     * @param idcard 待验证的身份证
     * @return 是否有效的18位身份证，忽略x的大小写
     */
    fun isValidCard18(idcard: String?): Boolean {
        return isValidCard18(idcard, true)
    }

    /**
     *
     *
     * 判断18位身份证的合法性
     *
     * 根据〖中华人民共和国国家标准GB11643-1999〗中有关公民身份号码的规定，公民身份号码是特征组合码，由十七位数字本体码和一位数字校验码组成。<br></br>
     * 排列顺序从左至右依次为：六位数字地址码，八位数字出生日期码，三位数字顺序码和一位数字校验码。
     *
     *
     * 顺序码: 表示在同一地址码所标识的区域范围内，对同年、同月、同 日出生的人编定的顺序号，顺序码的奇数分配给男性，偶数分配 给女性。
     *
     *
     *  1. 第1、2位数字表示：所在省份的代码
     *  1. 第3、4位数字表示：所在城市的代码
     *  1. 第5、6位数字表示：所在区县的代码
     *  1. 第7~14位数字表示：出生年、月、日
     *  1. 第15、16位数字表示：所在地的派出所的代码
     *  1. 第17位数字表示性别：奇数表示男性，偶数表示女性
     *  1. 第18位数字是校检码，用来检验身份证的正确性。校检码可以是0~9的数字，有时也用x表示
     *
     *
     *
     * 第十八位数字(校验码)的计算方法为：
     *
     *  1. 将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为：7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
     *  1. 将这17位数字和系数相乘的结果相加
     *  1. 用加出来和除以11，看余数是多少
     *  1. 余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2
     *  1. 通过上面得知如果余数是2，就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10，身份证的最后一位号码就是2
     *
     *
     * @param idcard     待验证的身份证
     * @param ignoreCase 是否忽略大小写。`true`则忽略X大小写，否则严格匹配大写。
     * @return 是否有效的18位身份证
     * @since 5.5.7
     */
    fun isValidCard18(idcard: String?, ignoreCase: Boolean): Boolean {
        if (idcard == null) {
            return false
        }
        if (CHINA_ID_MAX_LENGTH != idcard.length) {
            return false
        }

        // 截取省份代码。新版外国人永久居留身份证以9开头，第二三位是受理地代码
        val proCode = if (idcard.startsWith("9")) idcard.substring(1, 3) else idcard.substring(0, 2)
        if (null == CITY_CODES.get(proCode)) {
            return false
        }

        //校验生日
        if (false == T.isBirthday(idcard.substring(6, 14))) {
            return false
        }

        // 前17位
        val code17 = idcard.substring(0, 17)
        if (Regex.OnlyNumbersRule.matches( code17)) {
            // 获取校验位
            val `val` = getCheckCode18(code17)
            // 第18位
            return `val`.equals( idcard.get(17), ignoreCase)
        }
        return false
    }

    /**
     * 验证15位身份编码是否合法
     *
     * @param idcard 身份编码
     * @return 是否合法
     */
    fun isValidCard15(idcard: String?): Boolean {
        if (idcard == null) {
            return false
        }
        if (CHINA_ID_MIN_LENGTH != idcard.length) {
            return false
        }
        if (Regex.OnlyNumbersRule.matches(idcard)) {
            // 省份
            val proCode = idcard.substring(0, 2)
            if (null == CITY_CODES.get(proCode)) {
                return false
            }

            //校验生日（两位年份，补充为19XX）
            return false != T.isBirthday("19" + idcard.substring(6, 12))
        } else {
            return false
        }
    }

    /**
     * 验证10位身份编码是否合法
     *
     * @param idcard 身份编码
     * @return 身份证信息数组
     *
     *
     * [0] - 台湾、澳门、香港 [1] - 性别(男M,女F,未知N) [2] - 是否合法(合法true,不合法false) 若不是身份证件号码则返回null
     *
     */
    fun isValidCard10(idcard: String): Array<String?>? {
        var idcard = idcard
        if (idcard.isBlank()) {
            return null
        }

        // issue#IBP6T1 中文空格替换为英文
        idcard = idcard.replace( "（", "(")
        idcard = idcard.replace( "）", ")")

        val info = kotlin.arrayOfNulls<String>(3)
        val card = idcard.replace("[()]".toRegex(), "")
        if (card.length != 8 && card.length != 9 && idcard.length != 10) {
            return null
        }
        if (idcard.matches("^[a-zA-Z][0-9]{9}$".toRegex())) { // 台湾
            info[0] = "台湾"
            val char2 = idcard.get(1)
            if ('1' == char2) {
                info[1] = "M"
            } else if ('2' == char2) {
                info[1] = "F"
            } else {
                info[1] = "N"
                info[2] = "false"
                return info
            }
            info[2] = if (isValidTWCard(idcard)) "true" else "false"
        } else if (idcard.matches("^[157][0-9]{6}\\(?[0-9A-Z]\\)?$".toRegex())) { // 澳门
            info[0] = "澳门"
            info[1] = "N"
            info[2] = "true"
        } else if (idcard.matches("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?$".toRegex())) { // 香港
            info[0] = "香港"
            info[1] = "N"
            info[2] = if (isValidHKCard(idcard)) "true" else "false"
        } else {
            return null
        }
        return info
    }

    /**
     * 验证台湾身份证号码
     *
     * @param idcard 身份证号码
     * @return 验证码是否符合
     */
    fun isValidTWCard(idcard: String?): Boolean {
        if (null == idcard || idcard.length != 10) {
            return false
        }
        val iStart = TW_FIRST_CODE.get(idcard.get(0))
        if (null == iStart) {
            return false
        }
        var sum = iStart / 10 + (iStart % 10) * 9

        val mid = idcard.substring(1, 9)
        val chars = mid.toCharArray()
        var iflag = 8
        for (c in chars) {
            sum += c.toString().toInt() * iflag
            iflag--
        }

        val end = idcard.substring(9, 10)
        return (if (sum % 10 == 0) 0 else (10 - sum % 10)) == end.toInt()
    }

    /**
     * 验证香港身份证号码(存在Bug，部份特殊身份证无法检查)
     *
     *
     * 身份证前2位为英文字符，如果只出现一个英文字符则表示第一位是空格，对应数字58 前2位英文字符A-Z分别对应数字10-35 最后一位校验码为0-9的数字加上字符"A"，"A"代表10
     *
     *
     *
     * 将身份证号码全部转换为数字，分别对应乘9-1相加的总和，整除11则证件号码有效
     *
     *
     * @param idcard 身份证号码
     * @return 验证码是否符合
     */
    fun isValidHKCard(idcard: String): Boolean {
        if (!idcard.matches("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?$".toRegex())) {
            return false
        }

        var card = idcard.replace("[()]".toRegex(), "")
        var sum: Int
        if (card.length == 9) {
            sum = (card.get(0).uppercaseChar().code - 55) * 9 + (card.get(1).uppercaseChar().code - 55) * 8
            card = card.substring(1, 9)
        } else {
            sum = 522 + (card.get(0).uppercaseChar().code - 55) * 8
        }

        // 首字母A-Z，A表示1，以此类推
        val mid = card.substring(1, 7)
        val end = card.substring(7, 8)
        val chars = mid.toCharArray()
        var iflag = 7
        for (c in chars) {
            sum = sum + c.toString().toInt() * iflag
            iflag--
        }
        if ("A".equals(end, ignoreCase = true)) {
            sum += 10
        } else {
            sum += end.toInt()
        }
        return sum % 11 == 0
    }

    /**
     * 根据身份编号获取生日，只支持15或18位身份证号码
     *
     * @param idcard 身份编号
     * @return 生日(yyyyMMdd)
     * @see .getBirth
     */
    fun getBirthByIdCard(idcard: String): String? {
        return getBirth(idcard)
    }

    /**
     * 根据身份编号获取生日，只支持15或18位身份证号码
     *
     * @param idCard 身份编号
     * @return 生日(yyyyMMdd)
     */
    fun getBirth(idCard: String): String? {
        var idCard = idCard
        require(idCard.isNotBlank(), { "id card must be not blank!" })
        val len = idCard.length
        if (len < CHINA_ID_MIN_LENGTH) {
            return null
        } else if (len == CHINA_ID_MIN_LENGTH) {
            idCard = convert15To18(idCard)!!
        }

        return idCard.substring(6, 14)
    }

    /**
     * 从身份证号码中获取生日日期，只支持15或18位身份证号码
     *
     * @param idCard 身份证号码
     * @return 日期
     */
    fun getBirthDate(idCard: String): LocalDate? {
        val birthByIdCard = getBirthByIdCard(idCard)
        return if (null == birthByIdCard) null else LocalDate.Formats.yyMMdd.parse(birthByIdCard)
    }

    /**
     * 根据身份编号获取年龄，只支持15或18位身份证号码
     *
     * @param idcard 身份编号
     * @return 年龄
     */
    fun getAgeByIdCard(idcard: String): Int {
        return getAgeByIdCard(idcard, today)
    }

    /**
     * 根据身份编号获取指定日期当时的年龄年龄，只支持15或18位身份证号码
     *
     * @param idcard        身份编号
     * @param dateToCompare 以此日期为界，计算年龄。
     * @return 年龄
     */
    fun getAgeByIdCard(idcard: String, dateToCompare: LocalDate): Int {
        val birth = getBirthByIdCard(idcard)!!
        val birthDate = LocalDate.Formats.yyMMdd.parse(birth)
        return T.age(birthDate,dateToCompare)
    }

    /**
     * 根据身份编号获取生日年，只支持15或18位身份证号码
     *
     * @param idcard 身份编号
     * @return 生日(yyyy)
     */
    fun getYearByIdCard(idcard: String): Short? {
        var idcard = idcard
        val len = idcard.length
        if (len < CHINA_ID_MIN_LENGTH) {
            return null
        } else if (len == CHINA_ID_MIN_LENGTH) {
            idcard = convert15To18(idcard)!!
        }
        return idcard.substring(6, 10).toShort()
    }

    /**
     * 根据身份编号获取生日月，只支持15或18位身份证号码
     *
     * @param idcard 身份编号
     * @return 生日(MM)
     */
    fun getMonthByIdCard(idcard: String): Short? {
        var idcard = idcard
        val len = idcard.length
        if (len < CHINA_ID_MIN_LENGTH) {
            return null
        } else if (len == CHINA_ID_MIN_LENGTH) {
            idcard = convert15To18(idcard)!!
        }
        return idcard.substring(10, 12).toShort()
    }

    /**
     * 根据身份编号获取生日天，只支持15或18位身份证号码
     *
     * @param idcard 身份编号
     * @return 生日(dd)
     */
    fun getDayByIdCard(idcard: String): Short? {
        var idcard = idcard
        val len = idcard.length
        if (len < CHINA_ID_MIN_LENGTH) {
            return null
        } else if (len == CHINA_ID_MIN_LENGTH) {
            idcard = convert15To18(idcard)!!
        }
        return idcard.substring(12, 14).toShort()
    }

    /**
     * 根据身份编号获取性别，只支持15或18位身份证号码
     *
     * @param idcard 身份编号
     * @return 性别(1 : 男 ， 0 : 女)
     */
    fun getGenderByIdCard(idcard: String): Int {
        var idcard = idcard
        require(idcard.isNotBlank())
        val len = idcard.length
        require(len == CHINA_ID_MIN_LENGTH || len == CHINA_ID_MAX_LENGTH) { "ID Card length must be 15 or 18" }

        if (len == CHINA_ID_MIN_LENGTH) {
            idcard = convert15To18(idcard)!!
        }
        val sCardChar: Char = idcard.charAt(16)
        return if (sCardChar.code % 2 != 0) 1 else 0
    }

    /**
     * 根据身份编号获取户籍省份编码，只支持15或18位身份证号码
     *
     * @param idcard 身份编码
     * @return 省份编码
     * @since 5.7.2
     */
    fun getProvinceCodeByIdCard(idcard: String): String? {
        val len = idcard.length
        if (len == CHINA_ID_MIN_LENGTH || len == CHINA_ID_MAX_LENGTH) {
            return idcard.substring(0, 2)
        }
        return null
    }

    /**
     * 根据身份编号获取户籍省份，只支持15或18位身份证号码
     *
     * @param idcard 身份编码
     * @return 省份名称。
     */
    fun getProvinceByIdCard(idcard: String): String? {
        val code = getProvinceCodeByIdCard(idcard)
        if (code!!.isNotBlank()) {
            return CITY_CODES.get(code)
        }
        return null
    }

    /**
     * 根据身份编号获取地市级编码，只支持15或18位身份证号码<br></br>
     * 获取编码为4位
     *
     * @param idcard 身份编码
     * @return 地市级编码
     */
    fun getCityCodeByIdCard(idcard: String): String? {
        val len = idcard.length
        if (len == CHINA_ID_MIN_LENGTH || len == CHINA_ID_MAX_LENGTH) {
            return idcard.substring(0, 4)
        }
        return null
    }

    /**
     * 根据身份编号获取区县级编码，只支持15或18位身份证号码<br></br>
     * 获取编码为6位
     *
     * @param idcard 身份编码
     * @return 地市级编码
     * @since 5.8.0
     */
    fun getDistrictCodeByIdCard(idcard: String): String? {
        val len = idcard.length
        if (len == CHINA_ID_MIN_LENGTH || len == CHINA_ID_MAX_LENGTH) {
            return idcard.substring(0, 6)
        }
        return null
    }

    /**
     * 隐藏指定位置的几个身份证号数字为“*”
     *
     * @param idcard       身份证号
     * @param startInclude 开始位置（包含）
     * @param endExclude   结束位置（不包含）
     * @return 隐藏后的身份证号码
     * @see StrUtil.hide
     * @since 3.2.2
     */
//    fun hide(idcard: String?, startInclude: Int, endExclude: Int): String {
//        return hide(idcard, startInclude, endExclude)
//    }

    /**
     * 获取身份证信息，包括身份、城市代码、生日、性别等
     *
     * @param idcard 15或18位身份证
     * @return [Idcard]
     * @since 5.4.3
     */
    fun getIdcardInfo(idcard: String): Idcard {
        return Idcard(idcard)
    }

    // ----------------------------------------------------------------------------------- Private method start
    /**
     * 获得18位身份证校验码
     *
     * @param code17 18位身份证号中的前17位
     * @return 第18位
     */
    private fun getCheckCode18(code17: String): Char {
        val sum = getPowerSum(code17.toCharArray())
        return getCheckCode18(sum)
    }

    /**
     * 将power和值与11取模获得余数进行校验码判断
     *
     * @param iSum 加权和
     * @return 校验位
     */
    private fun getCheckCode18(iSum: Int): Char {
        when (iSum % 11) {
            10 -> return '2'
            9 -> return '3'
            8 -> return '4'
            7 -> return '5'
            6 -> return '6'
            5 -> return '7'
            4 -> return '8'
            3 -> return '9'
            2 -> return 'X'
            1 -> return '0'
            0 -> return '1'
            else -> return Char.SPACE
        }
    }

    /**
     * 将身份证的每位和对应位的加权因子相乘之后，再得到和值
     *
     * @param iArr 身份证号码的数组
     * @return 身份证编码
     */
    private fun getPowerSum(iArr: CharArray): Int {
        var iSum = 0
        if (POWER.size == iArr.size) {
            for (i in iArr.indices) {
                iSum += iArr[i].toString().toInt() * POWER[i]
            }
        }
        return iSum
    }

    // ----------------------------------------------------------------------------------- Private method end
    /**
     * 身份证信息，包括身份、城市代码、生日、性别等
     *
     * @author looly
     * @since 5.4.3
     */
    class Idcard(idcard: String){
        /**
         * 获取省份代码
         *
         * @return 省份代码
         */
        val provinceCode: String?

        /**
         * 获取市级编码
         *
         * @return 市级编码
         */
        val cityCode: String?
        private val birthDate: LocalDate?

        /**
         * 获取性别代号，性别(1 : 男 ， 0 : 女)
         *
         * @return 性别(1 : 男 ， 0 : 女)
         */
        val gender: Int

        /**
         * 获取年龄
         *
         * @return 年龄
         */
        val age: Int

        /**
         * 构造
         *
         * @param idcard 身份证号码
         */
        init {
            this.provinceCode = getProvinceCodeByIdCard(idcard)
            this.cityCode = getCityCodeByIdCard(idcard)
            this.birthDate = getBirthDate(idcard)
            this.gender = getGenderByIdCard(idcard)
            this.age = getAgeByIdCard(idcard)
        }

        val province: String
            /**
             * 获取省份名称
             *
             * @return 省份代码
             */
            get() = CITY_CODES.get(this.provinceCode)!!

        /**
         * 获得生日日期
         *
         * @return 生日日期
         */
        fun getBirthDate(): LocalDate? {
            return this.birthDate
        }

        override fun toString(): String {
            return "Idcard{" +
                    "provinceCode='" + provinceCode + '\'' +
                    ", cityCode='" + cityCode + '\'' +
                    ", birthDate=" + birthDate +
                    ", gender=" + gender +
                    ", age=" + age +
                    '}'
        }

    }
}
